{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = 'all'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.改变对象的字符串显示\n",
    "\n",
    "要改变一个实例的字符串表示，可重新定义它的 str() 和 repr() 方法。\n",
    "\n",
    "如果 str() 没有被定义，那么就会使用 repr() 来代替输出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Pair(3, 4)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'Pair(3, 4)'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'(3, 4)'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3, 4)\n"
     ]
    }
   ],
   "source": [
    "class Pair:\n",
    "    def __init__(self, x, y):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def __repr__(self):\n",
    "        return 'Pair({0.x!r}, {0.y!r})'.format(self)\n",
    "\n",
    "    def __str__(self):\n",
    "        return '({0.x!s}, {0.y!s})'.format(self)\n",
    "\n",
    "p = Pair(3, 4)\n",
    "p\n",
    "repr(p)\n",
    "\n",
    "str(p)\n",
    "print(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "p is Pair(3, 4)\n",
      "p is (3, 4)\n"
     ]
    }
   ],
   "source": [
    "# !r 格式化代码指明输出使用 repr() 来代替默认的 str() \n",
    "p = Pair(3, 4)\n",
    "print('p is {0!r}'.format(p))\n",
    "\n",
    "print('p is {0}'.format(p))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Pair(3, 4)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "Pair(3, 4)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# repr() 生成的文本字符串标准做法是需要让 eval(repr(x)) == x 为真。 \n",
    "p = Pair(3, 4)\n",
    "\n",
    "p\n",
    "eval(repr(p))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.自定义字符串的格式化\n",
    "\n",
    "为了自定义字符串的格式化，我们需要在类上面定义 format() 方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'2012-12-21'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'2012-12-21'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'The date is 2012-12-21'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'12/21/2012'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'The date is 12/21/2012'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'21/12/2012'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'The date is 21/12/2012'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'2012-12-21'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'Friday, December 21, 2012'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'The end is 21 Dec 2012. Goodbye'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "_formats = {\n",
    "    'ymd' : '{d.year}-{d.month}-{d.day}',\n",
    "    'mdy' : '{d.month}/{d.day}/{d.year}',\n",
    "    'dmy' : '{d.day}/{d.month}/{d.year}'\n",
    "    }\n",
    "\n",
    "class Date:\n",
    "    def __init__(self, year, month, day):\n",
    "        self.year = year\n",
    "        self.month = month\n",
    "        self.day = day\n",
    "\n",
    "    def __format__(self, code):\n",
    "        if code == '':\n",
    "            code = 'ymd'\n",
    "        fmt = _formats[code]\n",
    "        return fmt.format(d=self)\n",
    "\n",
    "d = Date(2012, 12, 21)\n",
    "format(d)\n",
    "\n",
    "format(d, 'ymd')\n",
    "'The date is {:ymd}'.format(d)\n",
    "\n",
    "format(d, 'mdy')\n",
    "'The date is {:mdy}'.format(d)\n",
    "\n",
    "format(d, 'dmy')\n",
    "'The date is {:dmy}'.format(d)\n",
    "\n",
    "print('-'*20)\n",
    "from datetime import date\n",
    "d = date(2012, 12, 21)\n",
    "format(d)\n",
    "format(d,'%A, %B %d, %Y')\n",
    "'The end is {:%d %b %Y}. Goodbye'.format(d)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.让对象支持上下文管理协议\n",
    "\n",
    "为了让一个对象兼容 with 语句，你需要实现 enter() 和 exit() 方法。 \n",
    "\n",
    "在需要管理一些资源比如文件、网络连接和锁的编程环境中，使用上下文管理器是很普遍的。 这些资源的一个主要特征是它们必须被手动的关闭或释放来确保程序的正确运行。 \n",
    "\n",
    "例如，如果你请求了一个锁，那么你必须确保之后释放了它，否则就可能产生死锁。 通过实现 enter() 和 exit() 方法并使用 with 语句可以很容易的避免这些问题， 因为 exit() 方法可以让你无需担心这些了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "26"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "22"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'HTTP/1.1 301 Moved Permanently\\r\\nServer: Varnish\\r\\nRetry-After: 0\\r\\nLocation: https://www.python.org/index.html\\r\\nContent-Length: 0\\r\\nAccept-Ranges: bytes\\r\\nDate: Tue, 12 Jul 2022 03:14:50 GMT\\r\\nVia: 1.1 varnish\\r\\nConnection: close\\r\\nX-Served-By: cache-nrt-rjtf7700068-NRT\\r\\nX-Cache: HIT\\r\\nX-Cache-Hits: 0\\r\\nX-Timer: S1657595690.254384,VS0,VE0\\r\\nStrict-Transport-Security: max-age=63072000; includeSubDomains\\r\\n\\r\\n'\n"
     ]
    }
   ],
   "source": [
    "from socket import socket, AF_INET, SOCK_STREAM\n",
    "\n",
    "class LazyConnection(object):\n",
    "    def __init__(self, address, family=AF_INET, type=SOCK_STREAM):\n",
    "        self.address = address\n",
    "        self.family = family\n",
    "        self.type = type\n",
    "        self.sock = None\n",
    "    \n",
    "    def __enter__(self):\n",
    "        if self.sock is not None:\n",
    "            raise RuntimeError('Already connected')\n",
    "        self.sock = socket(self.family, self.type)\n",
    "        self.sock.connect(self.address)\n",
    "        return self.sock\n",
    "    \n",
    "    def __exit__(self, exc_ty, exc_val, tb):\n",
    "        self.sock.close()\n",
    "        self.sock = None\n",
    "    \n",
    "# run\n",
    "from functools import partial\n",
    "\n",
    "conn = LazyConnection(('www.python.org', 80))\n",
    "with conn as s:\n",
    "    # conn.__enter__() executes: connection open\n",
    "    s.send(b'GET /index.html HTTP/1.0\\r\\n')\n",
    "    s.send(b'Host: www.python.org\\r\\n')\n",
    "    s.send(b'\\r\\n')\n",
    "    resp = b''.join(iter(partial(s.recv, 8192), b''))\n",
    "    print(resp)\n",
    "    # conn.__exit__() executes: connection closed"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.创建大量对象时节省内存方法\n",
    "\n",
    "你的程序要创建大量(可能上百万)的对象，导致占用很大的内存。\n",
    "\n",
    "对于主要是用来当成简单的数据结构的类而言，你可以通过给类添加 slots 属性来极大的减少实例所占的内存。\n",
    "\n",
    "当你定义 slots 后，Python就会为实例使用一种更加紧凑的内部表示。 实例通过一个很小的固定大小的数组来构建，而不是为每个实例定义一个字典，这跟元组或列表很类似。\n",
    "\n",
    "在 slots 中列出的属性名在内部被映射到这个数组的指定小标上。 使用slots一个不好的地方就是我们不能再给实例添加新的属性了，只能使用在 slots 中定义的那些属性名。\n",
    "\n",
    "尽管slots看上去是一个很有用的特性，很多时候你还是得减少对它的使用冲动。 Python的很多特性都依赖于普通的基于字典的实现。 \n",
    "\n",
    "另外，定义了slots后的类不再支持一些普通类特性了，比如多继承。 大多数情况下，你应该只在那些经常被使用到的用作数据结构的类上定义slots (比如在程序中需要创建某个类的几百万个实例对象)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Date:\n",
    "    __slots__ = ['year', 'month', 'day']\n",
    "    def __init__(self, year, month, day):\n",
    "        self.year = year\n",
    "        self.month = month\n",
    "        self.day = day\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.在类中封装属性名\n",
    "\n",
    "你想封装类的实例上面的“私有”数据，但是Python语言并没有访问控制。\n",
    "\n",
    "Python程序员不去依赖语言特性去封装数据，而是通过遵循一定的属性和方法命名规约来达到这个效果。 \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 第一个约定是任何以单下划线`_`开头的名字都应该是内部实现。\n",
    "# 同时还要注意到，使用下划线开头的约定同样适用于模块名和模块级别函数。\n",
    "\n",
    "class A:\n",
    "    def __init__(self):\n",
    "        self._internal = 0 # An internal attribute\n",
    "        self.public = 1 # A public attribute\n",
    "\n",
    "    def public_method(self):\n",
    "        '''\n",
    "        A public method\n",
    "        '''\n",
    "        pass\n",
    "\n",
    "    def _internal_method(self):\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用双下划线`__`开头的变量会导致访问名称变成其他形式。\n",
    "# 这种属性通过继承是无法被覆盖的。\n",
    "\n",
    "# 在下面的类B中，私有属性会被分别重命名为 _Bprivate 和 _Bprivate_method\n",
    "class B:\n",
    "    def __init__(self):\n",
    "        self.__private = 0\n",
    "\n",
    "    def __private_method(self):\n",
    "        pass\n",
    "\n",
    "    def public_method(self):\n",
    "        pass\n",
    "        self.__private_method()\n",
    "\n",
    "\n",
    "# 下面的类C中，私有名称 private 和 private_method 被重命名为 _Cprivate 和 _Cprivate_method\n",
    "# 这个跟父类B中的方法名称是完全不同的，所以也就无法覆盖B中类的方法\n",
    "class C(B):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.__private = 1 # Does not override B.__private\n",
    "\n",
    "    # Does not override B.__private_method()\n",
    "    def __private_method(self):\n",
    "        pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "到底哪种方式好呢？ \n",
    "\n",
    "大多数而言，你应该让你的非公共名称以单下划线开头。但是，如果你清楚你的代码会涉及到子类， 并且有些内部属性应该在子类中隐藏起来，那么才考虑使用双下划线方案。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.创建可管理的属性\n",
    "\n",
    "你想给某个实例attribute增加除访问与修改之外的其他处理逻辑，比如类型检查或合法性验证。\n",
    "\n",
    "自定义某个属性的一种简单方法是将它定义为一个property。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "call property\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'Guido'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------\n",
      "call first_name.setter\n",
      "call property\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'hello'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------\n",
      "call first_name.deleter\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "Can't delete attribute",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_1597682/2156556381.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     37\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     38\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-'\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 39\u001b[0;31m \u001b[0;32mdel\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfirst_name\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m/tmp/ipykernel_1597682/2156556381.py\u001b[0m in \u001b[0;36mfirst_name\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m     26\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mfirst_name\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     27\u001b[0m         \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'call first_name.deleter'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 28\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mAttributeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Can't delete attribute\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     29\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     30\u001b[0m \u001b[0;31m# property的一个关键特征是它看上去跟普通的attribute没什么两样， 但是访问它的时候会自动触发 getter 、setter 和 deleter 方法。\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: Can't delete attribute"
     ]
    }
   ],
   "source": [
    "# 代码中有三个相关联的方法，这三个方法的名字都必须一样。 \n",
    "# 第一个方法是一个 getter 函数，它使得 first_name 成为一个属性。 \n",
    "# 其他两个方法给 first_name 属性添加了 setter 和 deleter 函数。\n",
    "# 需要强调的是只有在 first_name 属性被创建后， 后面的两个装饰器 @first_name.setter 和 @first_name.deleter 才能被定义。\n",
    "\n",
    "class Person(object):\n",
    "    def __init__(self, first_name):\n",
    "        #  使用 self.first_name 而不是 self._first_name \n",
    "        # 目的是：在初始化的时候也进行这种类型检查。通过设置 self.first_name ，自动调用 setter 方法， setter 方法里面会进行参数的检查，\n",
    "        self.first_name = first_name\n",
    "    \n",
    "    # Getter function\n",
    "    @property\n",
    "    def first_name(self):\n",
    "        print('call property')\n",
    "        return self._first_name\n",
    "    \n",
    "    # Setter function\n",
    "    @first_name.setter\n",
    "    def first_name(self, value):\n",
    "        print('call first_name.setter')\n",
    "        if not isinstance(value, str):\n",
    "            raise TypeError('Expected a string')\n",
    "        self._first_name = value\n",
    "    \n",
    "    # Delete function\n",
    "    @first_name.deleter\n",
    "    def first_name(self):\n",
    "        print('call first_name.deleter')\n",
    "        raise AttributeError(\"Can't delete attribute\")\n",
    "\n",
    "# property的一个关键特征是它看上去跟普通的attribute没什么两样， 但是访问它的时候会自动触发 getter 、setter 和 deleter 方法。\n",
    "a = Person('Guido')\n",
    "a.first_name\n",
    "\n",
    "print('-'*20)\n",
    "a.first_name = 'hello'\n",
    "a.first_name\n",
    "\n",
    "print('-'*20)\n",
    "del a.first_name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'hellor'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'world'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 在已存在的get和set方法基础上定义property。\n",
    "\n",
    "class Person:\n",
    "    def __init__(self, first_name):\n",
    "        self.set_first_name(first_name)\n",
    "\n",
    "    # Getter function\n",
    "    def get_first_name(self):\n",
    "        return self._first_name\n",
    "\n",
    "    # Setter function\n",
    "    def set_first_name(self, value):\n",
    "        if not isinstance(value, str):\n",
    "            raise TypeError('Expected a string')\n",
    "        self._first_name = value\n",
    "\n",
    "    # Deleter function (optional)\n",
    "    def del_first_name(self):\n",
    "        raise AttributeError(\"Can't delete attribute\")\n",
    "\n",
    "    # Make a property from existing get/set methods\n",
    "    name = property(get_first_name, set_first_name, del_first_name)\n",
    "\n",
    "\n",
    "# test\n",
    "a = Person(\"hellor\")\n",
    "a.name\n",
    "\n",
    "a.name = 'world'\n",
    "a.name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.调用父类方法\n",
    "\n",
    "为了调用父类(超类)的一个方法，可以使用 super() 函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "B.spam\n",
      "A.spam\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(__main__.B, __main__.A, object)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class A:\n",
    "    def spam(self):\n",
    "        print('A.spam')\n",
    "\n",
    "class B(A):\n",
    "    def spam(self):\n",
    "        print('B.spam')\n",
    "        super().spam()\n",
    "\n",
    "b = B()\n",
    "b.spam()\n",
    "\n",
    "B.__mro__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8.子类中扩展property\n",
    "\n",
    "在子类中，你想要扩展定义在父类中的property的功能。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Setting name to Guido\n",
      "Getting name\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'Guido'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Setting name to Larry\n"
     ]
    }
   ],
   "source": [
    "class Person:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    # Getter function\n",
    "    @property\n",
    "    def name(self):\n",
    "        return self._name\n",
    "\n",
    "    # Setter function\n",
    "    @name.setter\n",
    "    def name(self, value):\n",
    "        if not isinstance(value, str):\n",
    "            raise TypeError('Expected a string')\n",
    "        self._name = value\n",
    "\n",
    "    # Deleter function\n",
    "    @name.deleter\n",
    "    def name(self):\n",
    "        raise AttributeError(\"Can't delete attribute\")\n",
    "\n",
    "class SubPerson(Person):\n",
    "    @property\n",
    "    def name(self):\n",
    "        print('Getting name')\n",
    "        return super().name\n",
    "\n",
    "    @name.setter\n",
    "    def name(self, value):\n",
    "        print('Setting name to', value)\n",
    "        super(SubPerson, SubPerson).name.__set__(self, value)\n",
    "\n",
    "    @name.deleter\n",
    "    def name(self):\n",
    "        print('Deleting name')\n",
    "        super(SubPerson, SubPerson).name.__delete__(self)\n",
    "\n",
    "# run test\n",
    "\n",
    "s = SubPerson('Guido')\n",
    "\n",
    "s.name\n",
    "\n",
    "s.name = 'Larry'\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "关于 `super(SubPerson, SubPerson)`的说明，必须提到`方法解析顺序(MRO)列表`。\n",
    "\n",
    "- https://github.com/yidao620c/python3-cookbook/blob/master/notebook/ipynb/%E7%AC%AC%E5%85%AB%E7%AB%A0%EF%BC%9A%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1/p07_calling_method_on_parent_class.ipynb\n",
    "- https://www.runoob.com/w3cnote/python-super-detail-intro.html\n",
    "\n",
    "**没看懂...以后需要再读一遍**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 9.创建新的类或实例属性\n",
    "\n",
    "你想创建一个新的拥有一些额外功能的实例属性类型，比如类型检查，可以通过一个描述器类的形式来定义它的功能。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Call <__main__.Point object at 0x7f2e64569940>.x.__set__, value=2\n",
      "Call <__main__.Point object at 0x7f2e64569940>.y.__set__, value=3\n",
      "Call <__main__.Point object at 0x7f2e64569940>.x.__get__,  cls=<class '__main__.Point'>\n",
      "Call <__main__.Point object at 0x7f2e64569940>.y.__get__,  cls=<class '__main__.Point'>\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'x': 2, 'y': 3}\n"
     ]
    }
   ],
   "source": [
    "# 一个描述器就是一个实现了三个核心的属性访问操作(get, set, delete)的类， 分别为 get() 、set() 和 delete() 这三个特殊的方法。 \n",
    "# 这些方法接受一个实例作为输入，之后相应的操作实例底层的字典。\n",
    "\n",
    "\n",
    "# Descriptor attribute for an integer type-checked attribute\n",
    "class Integer:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    \n",
    "    def __get__(self, instacne, cls):\n",
    "        print('Call {}.{}.__get__,  cls={}'.format(instacne, self.name, cls))\n",
    "        if instacne is None:\n",
    "            return self\n",
    "        else:\n",
    "            return instacne.__dict__[self.name]\n",
    "    \n",
    "    def __set__(self, instacne, value):\n",
    "        print('Call {}.{}.__set__, value={}'.format(instacne, self.name, value))\n",
    "        if not isinstance(value, int):\n",
    "            raise TypeError('Expected an int')\n",
    "        instacne.__dict__[self.name] = value\n",
    "\n",
    "    def __delete__(self, instance):\n",
    "        print('Call {}.__set__'.format(self.name))\n",
    "        del instance.__dict__[self.name]\n",
    "\n",
    "\n",
    "# 为了使用一个描述器，需将这个描述器的实例作为类属性放到一个类的定义中。\n",
    "class Point:\n",
    "\n",
    "    # 只能在类级别被定义，而不能为每个实例单独定义(即，不能在__init__方法里定义)\n",
    "    x = Integer('x')\n",
    "    y = Integer('y')\n",
    "\n",
    "    def __init__(self, x, y):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "# test\n",
    "p = Point(2, 3)\n",
    "p.x, p.y\n",
    "print(p.__dict__)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 10.使用延迟计算属性\n",
    "\n",
    "你想将一个只读属性定义成一个property，并且只在访问的时候才会计算结果。 但是一旦被访问后，你希望结果值被缓存起来，不用每次都去计算。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.0"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Computing area\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "50.26548245743669"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "50.26548245743669"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Computing perimeter\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "25.132741228718345"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "25.132741228718345"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 定义一个延迟属性的一种高效方法是通过使用一个描述器类\n",
    "# 当一个描述器被放入一个类的定义时， 每次访问属性时它的 get() 、set() 和 delete() 方法就会被触发。\n",
    "# 不过，如果一个描述器仅仅只定义了一个 get() 方法的话，它比通常的具有更弱的绑定。 \n",
    "# 特别地，只有当被访问属性不在实例底层的字典中时 get() 方法才会被触发。\n",
    "\n",
    "class lazyproperty:\n",
    "    def __init__(self, func):\n",
    "        self.func = func\n",
    "    \n",
    "    def __get__(self, instance, cls):\n",
    "        if instance is None:\n",
    "            return self\n",
    "        else:\n",
    "            value = self.func(instance)\n",
    "            setattr(instance, self.func.__name__, value)\n",
    "            return value\n",
    "\n",
    "\n",
    "import math\n",
    "\n",
    "class Circle:\n",
    "    def __init__(self, radius):\n",
    "        self.radius = radius\n",
    "\n",
    "    @lazyproperty\n",
    "    def area(self):\n",
    "        print('Computing area')\n",
    "        return math.pi * self.radius ** 2\n",
    "\n",
    "    @lazyproperty\n",
    "    def perimeter(self):\n",
    "        print('Computing perimeter')\n",
    "        return 2 * math.pi * self.radius\n",
    "\n",
    "\n",
    "c = Circle(4.0)\n",
    "c.radius\n",
    "\n",
    "# 消息 Computing area 和 Computing perimeter 仅仅出现一次\n",
    "c.area\n",
    "c.area\n",
    "c.perimeter\n",
    "c.perimeter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 11.简化数据结构的初始化\n",
    "\n",
    "你写了很多仅仅用作数据结构的类，不想写太多烦人的 init() 函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "Expected 3 arguments",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_2163117/2791944661.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     28\u001b[0m \u001b[0mp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPoint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     29\u001b[0m \u001b[0mc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mCircle\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4.5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 30\u001b[0;31m \u001b[0ms2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStock\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'ACME'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m50\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m/tmp/ipykernel_2163117/2791944661.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m      7\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_fields\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m             \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Expected {} arguments'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_fields\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     10\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m         \u001b[0;32mfor\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_fields\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTypeError\u001b[0m: Expected 3 arguments"
     ]
    }
   ],
   "source": [
    "# 可以在一个基类中写一个公用的 init() 函数\n",
    "import math\n",
    "\n",
    "class Structure1:\n",
    "    _fields = []\n",
    "\n",
    "    def __init__(self, *args):\n",
    "        if len(args) != len(self._fields):\n",
    "            raise TypeError('Expected {} arguments'.format(len(self._fields)))\n",
    "        \n",
    "        for name, value in zip(self._fields, args):\n",
    "            setattr(self, name, value)\n",
    "\n",
    "class Stock(Structure1):\n",
    "    _fields = ['name', 'shares', 'price']\n",
    "\n",
    "class Point(Structure1):\n",
    "    _fields = ['x', 'y']\n",
    "\n",
    "class Circle(Structure1):\n",
    "    _fields = ['radius']\n",
    "\n",
    "    def area(self):\n",
    "        return math.pi * self.radius ** 2\n",
    "\n",
    "# run test\n",
    "s = Stock('ACME', 50, 91.1)\n",
    "p = Point(2, 3)\n",
    "c = Circle(4.5)\n",
    "s2 = Stock('ACME', 50)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<__main__.Stock2 at 0x7f5a77917af0>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "ename": "TypeError",
     "evalue": "Invalid argument(s): aa",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_2163117/2110656529.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     26\u001b[0m \u001b[0ms2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStock2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'ACME'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m50\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mprice\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m91.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     27\u001b[0m \u001b[0ms3\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStock2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'ACME'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshares\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m50\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mprice\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m91.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 28\u001b[0;31m \u001b[0ms3\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStock2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'ACME'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshares\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m50\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mprice\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m91.1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maa\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m/tmp/ipykernel_2163117/2110656529.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m     17\u001b[0m         \u001b[0;31m# Check for any remaining unknown arguments\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     18\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 19\u001b[0;31m             \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Invalid argument(s): {}'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m','\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     21\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mStock2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mStructure2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTypeError\u001b[0m: Invalid argument(s): aa"
     ]
    }
   ],
   "source": [
    "# 如果还想支持关键字参数，可以将关键字参数设置为实例属性\n",
    "class Structure2:\n",
    "    _fields = []\n",
    "\n",
    "    def __init__(self, *args, **kwargs):\n",
    "        if len(args) > len(self._fields):\n",
    "            raise TypeError('Expected {} arguments'.format(len(self._fields)))\n",
    "\n",
    "        # Set all of the positional arguments\n",
    "        for name, value in zip(self._fields, args):\n",
    "            setattr(self, name, value)\n",
    "\n",
    "        # Set the remaining keyword arguments\n",
    "        for name in self._fields[len(args):]:\n",
    "            setattr(self, name, kwargs.pop(name))\n",
    "\n",
    "        # Check for any remaining unknown arguments\n",
    "        if kwargs:\n",
    "            raise TypeError('Invalid argument(s): {}'.format(','.join(kwargs)))\n",
    "\n",
    "class Stock2(Structure2):\n",
    "        _fields = ['name', 'shares', 'price']\n",
    "\n",
    "s1 = Stock2('ACME', 50, 91.1)\n",
    "s2 = Stock2('ACME', 50, price=91.1)\n",
    "s3 = Stock2('ACME', shares=50, price=91.1)\n",
    "s3 = Stock2('ACME', shares=50, price=91.1, aa=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 12.定义接口或者抽象基类\n",
    "\n",
    "你想定义一个接口或抽象类，并且通过执行类型检查来确保子类实现了某些特定的方法。\n",
    "\n",
    "使用 abc 模块可以很轻松的定义抽象基类。\n",
    "\n",
    "抽象类的一个特点是它不能直接被实例化，目的就是让别的类继承它并实现特定的抽象方法。\n",
    "\n",
    "抽象基类的一个主要用途是在代码中检查某些类是否为特定类型，实现了特定接口。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<__main__.SocketStream at 0x7f5a778bc070>"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.SocketStream object at 0x7f5a778bc070> isinstance of <class '__main__.IStream'>\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "io.IOBase"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "class IStream(metaclass=ABCMeta):\n",
    "    @abstractmethod\n",
    "    def read(self, maxbytes=-1):\n",
    "        pass\n",
    "\n",
    "    @abstractmethod\n",
    "    def write(self, data):\n",
    "        pass\n",
    "\n",
    "\n",
    "# 抽象类的一个特点是它不能直接被实例化\n",
    "# a = IStream()\n",
    "# TypeError: Can't instantiate abstract class IStream with abstract methods read, write\n",
    "\n",
    "\n",
    "# 抽象类的目的就是让别的类继承它并实现特定的抽象方法\n",
    "class SocketStream(IStream):\n",
    "    def read(self, maxbytes=-1):\n",
    "        pass\n",
    "\n",
    "    def write(self, data):\n",
    "        pass\n",
    "\n",
    "b = SocketStream()\n",
    "b\n",
    "\n",
    "# 抽象基类的一个主要用途是在代码中检查某些类是否为特定类型，实现了特定接口\n",
    "def seriaize(obj, stream):\n",
    "    if not isinstance(obj, stream):\n",
    "        raise TypeError('Expected an IStream')\n",
    "    print(str(obj) + \" isinstance of \" + str(stream) )\n",
    "    pass\n",
    "\n",
    "seriaize(b, IStream)\n",
    "\n",
    "\n",
    "# 还可以通过注册方式来让某个类实现抽象基类\n",
    "import io\n",
    "\n",
    "# Register the built-in I/O classes as supporting our interface\n",
    "IStream.register(io.IOBase)\n",
    "\n",
    "# Open a normal file and type check\n",
    "with open('/tmp/aaa.txt') as f:\n",
    "    isinstance(f, IStream) # Returns True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 13.实现数据模型的类型约束\n",
    "\n",
    "你想定义某些在属性赋值上面有限制的数据结构。\n",
    "\n",
    "在这个问题中，你需要在对某些实例属性赋值时进行检查。 所以你要自定义属性赋值函数，这种情况下最好使用描述器。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Base class. Uses a descriptor to set a value\n",
    "class Descriptor:\n",
    "    def __init__(self, name=None, **opt):\n",
    "        self.name = name\n",
    "        for key, value in opt.items():\n",
    "            setattr(self, key, value)\n",
    "        \n",
    "    def __set__(self, instance, value):\n",
    "        instance.__dict__[self.name] = value\n",
    "\n",
    "\n",
    "# Descriptor for enforcing types\n",
    "class Typed(Descriptor):\n",
    "    expected_type =type(None)\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if not isinstance(value, self.expected_type):\n",
    "            raise TypeError('expected ' + str(self.expected_type))\n",
    "        super().__set__(instance, value)\n",
    "\n",
    "# Descriptor for enforcing values\n",
    "class Unsigned(Descriptor):\n",
    "    def __set__(self, instance, value):\n",
    "        if value < 0:\n",
    "            raise ValueError('Expected >= 0')\n",
    "        super().__set__(instance, value)\n",
    "\n",
    "\n",
    "class MaxSized(Descriptor):\n",
    "    def __init__(self, name=None, **opts):\n",
    "        if 'size' not in opts:\n",
    "            raise TypeError('missing size option')\n",
    "        super().__init__(name, **opts)\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if len(value) >= self.size:\n",
    "            raise ValueError('size must be < ' + str(self.size))\n",
    "        super().__set__(instance, value)\n",
    "\n",
    "\n",
    "\n",
    "# 上面这些类就是你要创建的数据模型或类型系统的基础构建模块。\n",
    "# 下面就是我们实际定义的各种不同的数据类型\n",
    "class Integer(Typed):\n",
    "    expected_type = int\n",
    "\n",
    "class UnsignedInteger(Integer, Unsigned):\n",
    "    pass\n",
    "\n",
    "class Float(Typed):\n",
    "    expected_type = float\n",
    "\n",
    "class UnsignedFloat(Float, Unsigned):\n",
    "    pass\n",
    "\n",
    "class String(Typed):\n",
    "    expected_type = str\n",
    "\n",
    "class SizedString(String, MaxSized):\n",
    "    pass\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 方法1：\n",
    "# 使用这些自定义数据类型，我们定义一个类（也就是所谓的，类混入）\n",
    "class Stock:\n",
    "    name = String(\"name\", size=8)\n",
    "    shares = UnsignedInteger('shares')\n",
    "    price = UnsignedFloat('price')\n",
    "\n",
    "    def __init__(self, name, shares, price):\n",
    "        self.name = name\n",
    "        self.shares = shares\n",
    "        self.price = price\n",
    "\n",
    "# 然后测试这个类的属性赋值约束，可发现对某些属性的赋值违法了约束是不合法的\n",
    "s = Stock('apple', 12345, 150.12)\n",
    "s.name, s.shares, s.price\n",
    "\n",
    "s.name = 'ALIBB'\n",
    "s.name\n",
    "\n",
    "s.shares = -10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('apple', 12345, 150.12)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "'ALIBB'"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "ename": "ValueError",
     "evalue": "Expected >= 0",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_2163117/3126694673.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     31\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     32\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 33\u001b[0;31m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshares\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     34\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     35\u001b[0m \u001b[0;31m# 方法3：使用元类\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/tmp/ipykernel_2163117/626781396.py\u001b[0m in \u001b[0;36m__set__\u001b[0;34m(self, instance, value)\u001b[0m\n\u001b[1;32m     17\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpected_type\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     18\u001b[0m             \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'expected '\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpected_type\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 19\u001b[0;31m         \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__set__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minstance\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     21\u001b[0m \u001b[0;31m# Descriptor for enforcing values\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/tmp/ipykernel_2163117/626781396.py\u001b[0m in \u001b[0;36m__set__\u001b[0;34m(self, instance, value)\u001b[0m\n\u001b[1;32m     23\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m__set__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minstance\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     24\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 25\u001b[0;31m             \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Expected >= 0'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     26\u001b[0m         \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__set__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minstance\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     27\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mValueError\u001b[0m: Expected >= 0"
     ]
    }
   ],
   "source": [
    "# 方法2：\n",
    "# 使用类装饰器，也可以实现上述功能\n",
    "\n",
    "# Class decorator to apply constraints\n",
    "def check_attributes(**kwargs):\n",
    "    def decorate(cls):\n",
    "        for key, value in kwargs.items():\n",
    "            if isinstance(value, Descriptor):\n",
    "                value.name = key\n",
    "                setattr(cls, key, value)\n",
    "            else:\n",
    "                setattr(cls, key, value(key))\n",
    "        return cls\n",
    "\n",
    "    return decorate\n",
    "\n",
    "# Example\n",
    "@check_attributes(name=SizedString(size=8),\n",
    "                  shares=UnsignedInteger,\n",
    "                  price=UnsignedFloat)\n",
    "class Stock:\n",
    "    def __init__(self, name, shares, price):\n",
    "        self.name = name\n",
    "        self.shares = shares\n",
    "        self.price = price\n",
    "\n",
    "s = Stock('apple', 12345, 150.12)\n",
    "s.name, s.shares, s.price\n",
    "\n",
    "s.name = 'ALIBB'\n",
    "s.name\n",
    "\n",
    "s.shares = -10\n",
    "\n",
    "# 方法3：使用元类\n",
    "# 没看懂...\n",
    "\n",
    "# A metaclass that applies checking\n",
    "class checkedmeta(type):\n",
    "    def __new__(cls, clsname, bases, methods):\n",
    "        # Attach attribute names to the descriptors\n",
    "        for key, value in methods.items():\n",
    "            if isinstance(value, Descriptor):\n",
    "                value.name = key\n",
    "        return type.__new__(cls, clsname, bases, methods)\n",
    "\n",
    "# Example\n",
    "class Stock2(metaclass=checkedmeta):\n",
    "    name = SizedString(size=8)\n",
    "    shares = UnsignedInteger()\n",
    "    price = UnsignedFloat()\n",
    "\n",
    "    def __init__(self, name, shares, price):\n",
    "        self.name = name\n",
    "        self.shares = shares\n",
    "        self.price = price\n",
    "\n",
    "\n",
    "# 所有方法中，类装饰器方案应该是最灵活和最高明的。 \n",
    "# 首先，它并不依赖任何其他新的技术，比如元类。\n",
    "# 其次，装饰器可以很容易的添加或删除。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# 最后，装饰器还能作为混入类的替代技术来实现同样的效果\n",
    "# 这种方式定义的类跟之前的效果一样，而且执行速度会更快。 设置一个简单的类型属性的值，装饰器方式要比之前的混入类的方式几乎快100%。\n",
    "\n",
    "# Decorator for applying type checking\n",
    "def Typed(expected_type, cls=None):\n",
    "    if cls is None:\n",
    "        return lambda cls: Typed(expected_type, cls)\n",
    "    super_set = cls.__set__\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if not isinstance(value, expected_type):\n",
    "            raise TypeError('expected ' + str(expected_type))\n",
    "        super_set(self, instance, value)\n",
    "\n",
    "    cls.__set__ = __set__\n",
    "    return cls\n",
    "\n",
    "\n",
    "# Decorator for unsigned values\n",
    "def Unsigned(cls):\n",
    "    super_set = cls.__set__\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if value < 0:\n",
    "            raise ValueError('Expected >= 0')\n",
    "        super_set(self, instance, value)\n",
    "\n",
    "    cls.__set__ = __set__\n",
    "    return cls\n",
    "\n",
    "\n",
    "# Decorator for allowing sized values\n",
    "def MaxSized(cls):\n",
    "    super_init = cls.__init__\n",
    "\n",
    "    def __init__(self, name=None, **opts):\n",
    "        if 'size' not in opts:\n",
    "            raise TypeError('missing size option')\n",
    "        super_init(self, name, **opts)\n",
    "\n",
    "    cls.__init__ = __init__\n",
    "\n",
    "    super_set = cls.__set__\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if len(value) >= self.size:\n",
    "            raise ValueError('size must be < ' + str(self.size))\n",
    "        super_set(self, instance, value)\n",
    "\n",
    "    cls.__set__ = __set__\n",
    "    return cls\n",
    "\n",
    "\n",
    "# Specialized descriptors\n",
    "@Typed(int)\n",
    "class Integer(Descriptor):\n",
    "    pass\n",
    "\n",
    "\n",
    "@Unsigned\n",
    "class UnsignedInteger(Integer):\n",
    "    pass\n",
    "\n",
    "\n",
    "@Typed(float)\n",
    "class Float(Descriptor):\n",
    "    pass\n",
    "\n",
    "\n",
    "@Unsigned\n",
    "class UnsignedFloat(Float):\n",
    "    pass\n",
    "\n",
    "\n",
    "@Typed(str)\n",
    "class String(Descriptor):\n",
    "    pass\n",
    "\n",
    "\n",
    "@MaxSized\n",
    "class SizedString(String):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 14.实现自定义容器\n",
    "\n",
    "collections 定义了很多抽象基类，当你想自定义容器类的时候它们会非常有用。 \n",
    "\n",
    "collections 中很多抽象类会为一些常见容器操作提供默认的实现， 这样一来你只需要实现那些你最感兴趣的方法即可。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 15.属性的代理访问"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class A:\n",
    "    def spam(self, x):\n",
    "        pass\n",
    "\n",
    "    def foo(self):\n",
    "        pass\n",
    "\n",
    "\n",
    "class B1:\n",
    "    \"\"\"简单的代理\"\"\"\n",
    "\n",
    "    def __init__(self):\n",
    "        self._a = A()\n",
    "\n",
    "    def spam(self, x):\n",
    "        # Delegate to the internal self._a instance\n",
    "        return self._a.spam(x)\n",
    "\n",
    "    def foo(self):\n",
    "        # Delegate to the internal self._a instance\n",
    "        return self._a.foo()\n",
    "\n",
    "    def bar(self):\n",
    "        pass\n",
    "\n",
    "\n",
    "class B2:\n",
    "    \"\"\"使用__getattr__的代理, 代理方法比较多时候\"\"\"\n",
    "\n",
    "    def __init__(self):\n",
    "        self._a = A()\n",
    "\n",
    "    def bar(self):\n",
    "        pass\n",
    "\n",
    "    # Expose all of the methods defined on class A\n",
    "    def __getattr__(self, name):\n",
    "        \"\"\"这个方法在访问的attribute不存在的时候被调用\n",
    "        the __getattr__() method is actually a fallback method\n",
    "        that only gets called when an attribute is not found\"\"\"\n",
    "        return getattr(self._a, name)\n",
    "\n",
    "b1 = B1()\n",
    "b1.spam(1)\n",
    "\n",
    "b2 = B2()\n",
    "b2.spam(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "getattr: x\n",
      "2\n",
      "getattr: bar\n",
      "Spam.bar: 2 3\n",
      "setattr: x 37\n"
     ]
    }
   ],
   "source": [
    "# 代理模式\n",
    "class Proxy:\n",
    "    def __init__(self, obj):\n",
    "        self._obj = obj\n",
    "    \n",
    "    # Delegate attribute lookup to internal obj\n",
    "    def __getattr__(self, name):\n",
    "        print('getattr:', name)\n",
    "        return getattr(self._obj, name)\n",
    "    \n",
    "    # Delegate attribute assignment\n",
    "    def __setattr__(self, name, value):\n",
    "        if name.startswith('_'):\n",
    "            super().__setattr__(name, value)\n",
    "        else:\n",
    "            print('setattr:', name, value)\n",
    "            setattr(self._obj, name, value)\n",
    "    \n",
    "    # Delegate attribute deletion\n",
    "    def __delattr__(self, name):\n",
    "        if name.startswith('_'):\n",
    "            super().__delattr__(name)\n",
    "        else:\n",
    "            print('delattr:', name)\n",
    "            delattr(self._obj, name)\n",
    "    \n",
    "\n",
    "# 使用这个代理类时，你只需要用它来包装下其他类即可\n",
    "class Spam:\n",
    "    def __init__(self, x):\n",
    "        self.x = x\n",
    "\n",
    "    def bar(self, y):\n",
    "        print('Spam.bar:', self.x, y)\n",
    "\n",
    "s = Spam(2)\n",
    "p = Proxy(s)\n",
    "\n",
    "print(p.x)\n",
    "p.bar(3)\n",
    "p.x = 37"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 16.在类中定义多个构造器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<__main__.Date at 0x7f7699ea1520>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "<__main__.Date at 0x7f7699ea1430>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 为了实现多个构造器，你需要使用到类方法。\n",
    "# 类方法的一个主要用途就是定义多个构造器。它接受一个 class 作为第一个参数(cls)。 \n",
    "\n",
    "import time\n",
    "\n",
    "class Date:\n",
    "\n",
    "    # Primary constructor\n",
    "    def __init__(self, year, month, day):\n",
    "        self.year = year\n",
    "        self.month = month\n",
    "        self.day = day\n",
    "\n",
    "    # Alternate constructor\n",
    "    @classmethod\n",
    "    def today(cls):\n",
    "        t = time.localtime()\n",
    "        return cls(t.tm_year, t.tm_mon, t.tm_mday)\n",
    "    \n",
    "# Primary\n",
    "a = Date(2012, 12, 21) \n",
    "a\n",
    "\n",
    "# Alternate\n",
    "b = Date.today()\n",
    "b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 17.创建不调用init方法的实例\n",
    "\n",
    "你想创建一个实例，但是希望绕过执行 init() 方法。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2012"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 可以通过 new() 方法创建一个未初始化的实例。\n",
    "\n",
    "class Date:\n",
    "    def __init__(self, year, month, day):\n",
    "        self.year = year\n",
    "        self.month = month\n",
    "        self.day = day\n",
    "\n",
    "d = Date.__new__(Date)\n",
    "\n",
    "# AttributeError: 'Date' object has no attribute 'year'\n",
    "# d.year\n",
    "\n",
    "data = {'year':2012, 'month':8, 'day':29}\n",
    "for key, value in data.items():\n",
    "    setattr(d, key, value)\n",
    "d.year"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 18.利用Mixins扩展类功能\n",
    "\n",
    "你有很多有用的方法，想使用它们来扩展其他类的功能。但是这些类并没有任何继承的关系。 因此你不能简单的将这些方法放入一个基类，然后被其他类继承。\n",
    "\n",
    "对于混入类，有几点需要记住。\n",
    "- 首先是，混入类不能直接被实例化使用。 \n",
    "- 其次，混入类没有自己的状态信息，也就是说它们并没有定义 __init__() 方法，并且没有实例属性。 这也是为什么我们在上面明确定义了 __slots__ = () 。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Setting x = 23\n",
      "Getting x\n",
      "23\n",
      "Deleting x\n",
      "Setting x = 23\n",
      "Getting x\n",
      "23\n",
      "Deleting x\n"
     ]
    }
   ],
   "source": [
    "# 假设你想扩展映射对象，给它们添加日志、唯一性设置、类型检查等等功能。\n",
    "# 下面是一些混入类：\n",
    "\n",
    "class LoggedMappingMixin:\n",
    "    \"\"\"\n",
    "    Add logging to get/set/delete operations for debugging.\n",
    "    \"\"\"\n",
    "    __slots__ = ()  # 混入类都没有实例变量，因为直接实例化混入类没有任何意义\n",
    "\n",
    "    def __getitem__(self, key):\n",
    "        print('Getting ' + str(key))\n",
    "        return super().__getitem__(key)\n",
    "\n",
    "    def __setitem__(self, key, value):\n",
    "        print('Setting {} = {!r}'.format(key, value))\n",
    "        return super().__setitem__(key, value)\n",
    "\n",
    "    def __delitem__(self, key):\n",
    "        print('Deleting ' + str(key))\n",
    "        return super().__delitem__(key)\n",
    "\n",
    "\n",
    "class SetOnceMappingMixin:\n",
    "    '''\n",
    "    Only allow a key to be set once.\n",
    "    '''\n",
    "    __slots__ = ()\n",
    "\n",
    "    def __setitem__(self, key, value):\n",
    "        if key in self:\n",
    "            raise KeyError(str(key) + ' already set')\n",
    "        return super().__setitem__(key, value)\n",
    "\n",
    "\n",
    "class StringKeysMappingMixin:\n",
    "    '''\n",
    "    Restrict keys to strings only\n",
    "    '''\n",
    "    __slots__ = ()\n",
    "\n",
    "    def __setitem__(self, key, value):\n",
    "        if not isinstance(key, str):\n",
    "            raise TypeError('keys must be strings')\n",
    "        return super().__setitem__(key, value)\n",
    "\n",
    "# 这些类单独使用起来没有任何意义，事实上如果你去实例化任何一个类，除了产生异常外没任何作用。 它们是用来通过多继承来和其他映射对象混入使用的。\n",
    "\n",
    "class LoggedDict1(LoggedMappingMixin, dict):\n",
    "    pass\n",
    "\n",
    "d1 = LoggedDict1()\n",
    "d1['x'] = 23\n",
    "print(d1['x'])\n",
    "del d1['x']\n",
    "\n",
    "class LoggedDict2(dict, LoggedMappingMixin):\n",
    "    pass\n",
    "\n",
    "d2 = LoggedDict1()\n",
    "d2['x'] = 23\n",
    "print(d2['x'])\n",
    "del d2['x']\n",
    "\n",
    "from collections import defaultdict\n",
    "\n",
    "class SetOnceDefaultDict(SetOnceMappingMixin, defaultdict):\n",
    "    pass\n",
    "\n",
    "d = SetOnceDefaultDict(list)\n",
    "d['x'].append(2)\n",
    "d['x'].append(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Setting x = 1\n",
      "Getting x\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Deleting x\n"
     ]
    }
   ],
   "source": [
    "# 另一种实现混入类的方式就是使用类装饰器\n",
    "def LoggedMapping(cls):\n",
    "    \"\"\"第二种方式：使用类装饰器\"\"\"\n",
    "    cls_getitem = cls.__getitem__\n",
    "    cls_setitem = cls.__setitem__\n",
    "    cls_delitem = cls.__delitem__\n",
    "\n",
    "    def __getitem__(self, key):\n",
    "        print('Getting ' + str(key))\n",
    "        return cls_getitem(self, key)\n",
    "\n",
    "    def __setitem__(self, key, value):\n",
    "        print('Setting {} = {!r}'.format(key, value))\n",
    "        return cls_setitem(self, key, value)\n",
    "\n",
    "    def __delitem__(self, key):\n",
    "        print('Deleting ' + str(key))\n",
    "        return cls_delitem(self, key)\n",
    "\n",
    "    cls.__getitem__ = __getitem__\n",
    "    cls.__setitem__ = __setitem__\n",
    "    cls.__delitem__ = __delitem__\n",
    "    return cls\n",
    "\n",
    "\n",
    "@LoggedMapping\n",
    "class LoggedDict3(dict):\n",
    "    pass\n",
    "\n",
    "dd = LoggedDict3()\n",
    "dd['x'] = 1\n",
    "dd['x']\n",
    "del dd['x']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 19.实现状态对象或者状态机\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.ClosedConnectionState"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "__main__.OpenConnectionState"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "reading\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "__main__.OpenConnectionState"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "writing\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "__main__.OpenConnectionState"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "__main__.ClosedConnectionState"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class Connection1:\n",
    "    \"\"\"新方案——对每个状态定义一个类\"\"\"\n",
    "\n",
    "    def __init__(self):\n",
    "        self.new_state(ClosedConnectionState)\n",
    "\n",
    "    def new_state(self, newstate):\n",
    "        self._state = newstate\n",
    "        # Delegate to the state class\n",
    "\n",
    "    def read(self):\n",
    "        return self._state.read(self)\n",
    "\n",
    "    def write(self, data):\n",
    "        return self._state.write(self, data)\n",
    "\n",
    "    def open(self):\n",
    "        return self._state.open(self)\n",
    "\n",
    "    def close(self):\n",
    "        return self._state.close(self)\n",
    "\n",
    "\n",
    "# Connection state base class\n",
    "class ConnectionState:\n",
    "    @staticmethod\n",
    "    def read(conn):\n",
    "        raise NotImplementedError()\n",
    "\n",
    "    @staticmethod\n",
    "    def write(conn, data):\n",
    "        raise NotImplementedError()\n",
    "\n",
    "    @staticmethod\n",
    "    def open(conn):\n",
    "        raise NotImplementedError()\n",
    "\n",
    "    @staticmethod\n",
    "    def close(conn):\n",
    "        raise NotImplementedError()\n",
    "\n",
    "\n",
    "# Implementation of different states\n",
    "class ClosedConnectionState(ConnectionState):\n",
    "    @staticmethod\n",
    "    def read(conn):\n",
    "        raise RuntimeError('Not open')\n",
    "\n",
    "    @staticmethod\n",
    "    def write(conn, data):\n",
    "        raise RuntimeError('Not open')\n",
    "\n",
    "    @staticmethod\n",
    "    def open(conn):\n",
    "        conn.new_state(OpenConnectionState)\n",
    "\n",
    "    @staticmethod\n",
    "    def close(conn):\n",
    "        raise RuntimeError('Already closed')\n",
    "\n",
    "\n",
    "class OpenConnectionState(ConnectionState):\n",
    "    @staticmethod\n",
    "    def read(conn):\n",
    "        print('reading')\n",
    "\n",
    "    @staticmethod\n",
    "    def write(conn, data):\n",
    "        print('writing')\n",
    "\n",
    "    @staticmethod\n",
    "    def open(conn):\n",
    "        raise RuntimeError('Already open')\n",
    "\n",
    "    @staticmethod\n",
    "    def close(conn):\n",
    "        conn.new_state(ClosedConnectionState)\n",
    "\n",
    "c = Connection1()\n",
    "c._state\n",
    "c.open()\n",
    "c._state\n",
    "c.read()\n",
    "c._state\n",
    "c.write('1234')\n",
    "c._state\n",
    "c.close()\n",
    "c._state"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 20.通过字符串调用对象方法\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.6055512754639896"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "3.6055512754639896"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "import math\n",
    "\n",
    "class Point:\n",
    "    def __init__(self, x, y):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def __repr__(self):\n",
    "        return 'Point({!r:},{!r:})'.format(self.x, self.y)\n",
    "\n",
    "    def distance(self, x, y):\n",
    "        return math.hypot(self.x - x, self.y - y)\n",
    "\n",
    "\n",
    "# 最简单的情况，可以使用 getattr()\n",
    "p = Point(2, 3)\n",
    "d = getattr(p, 'distance')(0, 0)  # Calls p.distance(0, 0)\n",
    "d\n",
    "\n",
    "\n",
    "# 另外一种方法是使用 operator.methodcaller()\n",
    "import operator\n",
    "operator.methodcaller('distance', 0, 0)(p)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 21.实现访问者模式\n",
    "\n",
    "你要处理由大量不同类型的对象组成的复杂数据结构，每一个对象都需要进行不同的处理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('PUSH', 1),\n",
       " ('PUSH', 2),\n",
       " ('PUSH', 3),\n",
       " ('PUSH', 4),\n",
       " ('SUB',),\n",
       " ('MUL',),\n",
       " ('PUSH', 5),\n",
       " ('DIV',),\n",
       " ('ADD',)]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class Node:\n",
    "    pass\n",
    "\n",
    "class BinaryOperator(Node):\n",
    "    def __init__(self, left, right):\n",
    "        self.left = left\n",
    "        self.right = right\n",
    "\n",
    "class Add(BinaryOperator):\n",
    "    pass\n",
    "\n",
    "class Sub(BinaryOperator):\n",
    "    pass\n",
    "\n",
    "class Mul(BinaryOperator):\n",
    "    pass\n",
    "\n",
    "class Div(BinaryOperator):\n",
    "    pass\n",
    "\n",
    "\n",
    "class Number(Node):\n",
    "    def __init__(self, value):\n",
    "        self.value = value\n",
    "\n",
    "class NodeVisitor:\n",
    "    def visit(self, node):\n",
    "        methname = 'visit_' + type(node).__name__\n",
    "        meth = getattr(self, methname, None)\n",
    "        if meth is None:\n",
    "            meth = self.generic_visit\n",
    "        return meth(node)\n",
    "\n",
    "    def generic_visit(self, node):\n",
    "        raise RuntimeError('No {} method'.format('visit_' + type(node).__name__))\n",
    "\n",
    "\n",
    "class StackCode(NodeVisitor):\n",
    "    def generate_code(self, node):\n",
    "        self.instructions = []\n",
    "        self.visit(node)\n",
    "        return self.instructions\n",
    "\n",
    "    def visit_Number(self, node):\n",
    "        self.instructions.append(('PUSH', node.value))\n",
    "\n",
    "    def binop(self, node, instruction):\n",
    "        self.visit(node.left)\n",
    "        self.visit(node.right)\n",
    "        self.instructions.append((instruction,))\n",
    "\n",
    "    def visit_Add(self, node):\n",
    "        self.binop(node, 'ADD')\n",
    "\n",
    "    def visit_Sub(self, node):\n",
    "        self.binop(node, 'SUB')\n",
    "\n",
    "    def visit_Mul(self, node):\n",
    "        self.binop(node, 'MUL')\n",
    "\n",
    "    def visit_Div(self, node):\n",
    "        self.binop(node, 'DIV')\n",
    "\n",
    "    def unaryop(self, node, instruction):\n",
    "        self.visit(node.operand)\n",
    "        self.instructions.append((instruction,))\n",
    "\n",
    "    def visit_Negate(self, node):\n",
    "        self.unaryop(node, 'NEG')\n",
    "\n",
    "# Representation of 1 + 2 * (3 - 4) / 5\n",
    "t1 = Sub(Number(3), Number(4))\n",
    "t2 = Mul(Number(2), t1)\n",
    "t3 = Div(t2, Number(5))\n",
    "t4 = Add(Number(1), t3)\n",
    "\n",
    "s = StackCode()\n",
    "s.generate_code(t4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 22.不用递归实现访问者模式\n",
    "\n",
    "https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p22_implementing_visitor_pattern_without_recursion.html\n",
    "\n",
    "没看懂。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 23.循环引用数据结构的内存管理\n",
    "\n",
    "https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p23_managing_memory_in_cyclic_data_structures.html\n",
    "\n",
    "使用 weakref 库中的弱引用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 24.让类支持比较操作\n",
    "\n",
    "你想让某个类的实例支持标准的比较运算(比如>=,!=,<=,<等)，但是又不想去实现那一大丢的特殊方法。\n",
    "\n",
    "装饰器 functools.total_ordering 就是用来简化这个处理的。 使用它来装饰一个类，你只需定义一个 __eq__() 方法， 外加其他方法(__lt__, __le__, __gt__, or __ge__)中的一个即可。 \n",
    "\n",
    "然后装饰器会自动为你填充其它比较方法。\n",
    "\n",
    "https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p24_making_classes_support_comparison_operations.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 25.创建缓存实例\n",
    "\n",
    "在创建一个类的对象时，如果之前使用同样参数创建过这个对象， 你想返回它的缓存引用。\n",
    "\n",
    "这种情况通常是因为你希望相同参数创建的对象是单例。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 为了达到这样的效果，你需要使用一个和类本身分开的工厂函数\n",
    "# The class in question\n",
    "class Spam:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "# Caching support\n",
    "import weakref\n",
    "_spam_cache = weakref.WeakValueDictionary()\n",
    "def get_spam(name):\n",
    "    if name not in _spam_cache:\n",
    "        s = Spam(name)\n",
    "        _spam_cache[name] = s\n",
    "    else:\n",
    "        s = _spam_cache[name]\n",
    "    return s\n",
    "\n",
    "\n",
    "a = get_spam('foo')\n",
    "b = get_spam('bar')\n",
    "a is b\n",
    "\n",
    "c = get_spam('foo')\n",
    "a is c"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.9.7 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "caf1c2fcf97217de91eafa76b907d50f9ea378f5ffbee7f571142d119bb6a771"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
